🤖 feat: expose template source files via CoderTemplate spec.files#69
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dfc03f7520
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both review comments:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a8b29e0d60
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P1: Added post-condition verification after |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 98564f6cde
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P1: Replaced |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e890c4ec5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both comments:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b118a4b9d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P1: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84df43ce9b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P1: Reordered |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd380c9aab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both comments:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9b42a34d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed P2: Moved duplicate normalized-path detection in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa23172add
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both P2 comments:
|
|
Codex Review: Didn't find any major issues. What shall we delve into next? ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Expose Coder template source files via the aggregated API by adding
spec.files(amap[string]stringof relative paths → UTF-8 contents) toCoderTemplate. This enableskubectl get codertemplate <org>.<name> -o yamlto include template source, and enables GitOps controllers to create/update templates by specifying source files inline.Background
The aggregated API server already exposes
CoderTemplateresources with template metadata, but users and GitOps tooling couldn't view or manage the template's actual source tree (Terraform files, Dockerfiles, etc.) through the Kubernetes API. This change bridges that gap.Implementation
API type (
api/aggregation/v1alpha1/types.go)Files map[string]stringfield toCoderTemplateSpecwithjson:"files,omitempty"make codegenStorage helper (
internal/aggregated/storage/template_files.go)fetchTemplateSourceFiles(): downloads the active version's source zip viacodersdk.TemplateVersion→codersdk.DownloadWithFormat, unpacks, validates paths/sizes, filters non-UTF8 filesbuildSourceZip(): creates deterministic zip from file map for uploadStorage Get/Create/Update (
internal/aggregated/storage/template.go)spec.filesfrom the active version's source archivespec.filesto keep responses smalldisplayName,description,iconviaUpdateTemplateMetaConvert helper (
internal/aggregated/convert/template.go)TemplateUpdateMetaRequestFromK8s()for building metadata update payloadsTests (
internal/aggregated/storage/storage_test.go)TestTemplateStorageGetPopulatesSpecFiles,TestTemplateStorageListOmitsSpecFiles,TestTemplateStorageCreateWithFiles,TestTemplateStorageUpdateWithChangedFiles,TestTemplateStorageUpdateWithIdenticalFilesIsNoOp,TestTemplateStorageUpdateMetadataDocs
docs/reference/api/codertemplate.mdto include the new fieldValidation
make verify-vendor✅make build✅make test✅make lint✅make docs-check✅Risks
/sourcesubresource could mitigate this.📋 Implementation Plan
Plan: Expose Coder template source files via
CoderTemplate.spec.filesContext / Why
Today the aggregated API server exposes
CoderTemplateandCoderWorkspaceas Kubernetes-native resources, but it only returns template metadata (name, description, version IDs, etc.). In the Coder UI, users can view a template version’s source tree (Terraform, Dockerfile, etc.).We want
kubectl get codertemplate <org>.<name> -o yamlto include the template’s source files so downstream tooling/UIs can display the same information via the aggregated API.Chosen approach (Option A): Add an inline
spec.filesmap (path → file contents) toCoderTemplateand have the aggregated API server populate it from the active template version’s source archive stored in Coder.Evidence (repo + SDK)
api/aggregation/v1alpha1/types.godefinesCoderTemplateSpec/Statusbut has no source fields.api/aggregation/v1alpha1/zz_generated.deepcopy.gois generated and will need regen after API changes.internal/aggregated/storage/template.goGet()calls Coder (sdk.TemplateByName) and returnsconvert.TemplateToK8s(...).List()callssdk.Templates(...)and converts each template; we should avoid per-template source downloads here.internal/aggregated/convert/template.gomapscodersdk.Template.ActiveVersionID→CoderTemplate.spec.versionID.(*codersdk.Client).TemplateVersion(ctx, id)invendor/github.com/coder/coder/v2/codersdk/templateversions.go.TemplateVersion.Job.FileID(source archive ID) invendor/.../codersdk/provisionerdaemons.go.(*codersdk.Client).DownloadWithFormat(ctx, fileID, codersdk.FormatZip)invendor/.../codersdk/files.go.CoderTemplatefields:docs/reference/api/codertemplate.md(generated).Implementation details
1) API: add
spec.filesfield (text-only)File:
api/aggregation/v1alpha1/types.goAdd a new optional field to
CoderTemplateSpec:Notes/constraints to bake into the implementation:
map[string]CoderTemplateFile{content, encoding}) rather than stuffing base64 into a string.Generated artifacts:
api/aggregation/v1alpha1/zz_generated.deepcopy.go(viamake codegen).docs/reference/api/codertemplate.md(viamake docs-referenceor the repo’s canonical docs target).2) Storage: populate
spec.filesinTemplateStorage.Get()onlyFile:
internal/aggregated/storage/template.goChange
Get()from returningconvert.TemplateToK8s(...)directly to:sdk.TemplateVersion(ctx, template.ActiveVersionID)).sdk.DownloadWithFormat(..., codersdk.FormatZip)).obj.Spec.Files = files.Shape:
Helper: unzip + safety limits
Add a helper function (either near
TemplateStorageor in a small internal helper file likeinternal/aggregated/storage/template_files.go) to keepGet()readable:Trade-offs of raising limits (e.g. 20–40 MiB archives)
codersdk.DownloadWithFormatbuffers the entire archive in memory; larger limits increase per-request allocations and GC pressure.kubectl/GitOps controllers to render/diff,--max-request-bytes), so very large YAML manifests may be rejected before reaching the aggregated API server.If we need to support very large templates, we should consider:
/sourcesubresource that streams zip/tar instead of inlining all file contents.Defensive programming expectations:
Keep LIST fast
File:
internal/aggregated/storage/template.goDo not populate
spec.filesinList():kubectl get codertemplates -o yamlhuge.So
List()continues to callconvert.TemplateToK8s(...)and returns objects withspec.filesunset.3) GitOps: support creating/updating templates from
spec.filesFiles:
internal/aggregated/storage/template.go,internal/aggregated/convert/template.goGoal: enable GitOps controllers (ArgoCD/Flux) and
kubectl applyto create and reconcile Coder templates from YAML by treatingspec.filesas the desired template source of truth.CREATE behavior (template does not exist yet)
In
TemplateStorage.Create(...):spec.filesis provided (len > 0):spec.files.sdk.Upload(ctx, codersdk.ContentTypeZip, reader)→fileID.sdk.CreateTemplateVersion(ctx, org.ID, codersdk.CreateTemplateVersionRequest{ StorageMethod: file, FileID: fileID, Provisioner: terraform })sdk.CreateTemplate(ctx, org.ID, codersdk.CreateTemplateRequest{ Name: templateName, VersionID: templateVersion.ID, ...meta })spec.versionID.UPDATE behavior (reconcile template to match YAML)
Expand
TemplateStorage.Update(...)beyond the current “spec.runningonly” legacy behavior:Metadata reconcile
spec.displayName,spec.description, andspec.iconto be updated viasdk.UpdateTemplateMeta(...).metadata.name+spec.organizationimmutable (still derived from name).Source reconcile (
spec.files)spec.files:fetchTemplateSourceFileshelper)./api/v2/files).TemplateID: template.ID).sdk.UpdateActiveTemplateVersion(template.ID, codersdk.UpdateActiveTemplateVersion{ID: newVersion.ID}).Patch / server-side apply compatibility
kubectl applyand a JSON merge-patch update flow work end-to-end.TemplateStoragealready satisfiesrest.Patcher(Getter+Updater), but we should verify managedFields/resourceVersion behavior in practice.Notes on idempotency + drift
job.file_id) to short-circuit equality checks.4) Tests: extend mock Coder server to serve template source archive
File:
internal/aggregated/storage/storage_test.goThe existing
newMockCoderServer()already supports template CRUD plus:GET /api/v2/templateversions/{id}Extend it to also support GitOps + source flows:
GET /api/v2/files/{fileID}?format=zip(download source archive)POST /api/v2/files(upload source archive)POST /api/v2/organizations/{orgID}/templateversions(create template version)PATCH /api/v2/templates/{templateID}(update template metadata)PATCH /api/v2/templates/{templateID}/versions(promote active version)Implementation approach:
filesByID map[uuid.UUID][]bytein mock state and seed it with a deterministic zip for the starter template version.hashJSON field.TemplateVersion.Job.FileIDfrom the request’sFileIDand optionally attach it to a template whenTemplateIDis set.Add/extend tests:
Get()returnsspec.fileswith expected contents (e.g.main.tf).List()omitsspec.files.spec.filessucceeds and subsequent GET returns those files.spec.filescreates a new template version + promotes it; UPDATE with identical files is a no-op (GitOps idempotency).5) Docs + examples
docs/reference/api/codertemplate.mdincludes the newspec.filesfield.docs/page or README note explaining:spec.filesis the desired template source for GitOps. The server returns it on GET but omits it on LIST.spec.versionIDreflects the active version and may change when source changes (configure Argo/Flux ignore rules if needed).getoncodertemplates).Validation checklist (what to run in Exec mode)
make codegenmake docs-reference(or repo docs target)make testmake lintGenerated with
mux• Model:anthropic:claude-opus-4-6• Thinking:xhigh• Cost:$0.92